/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_loadctrl.h"

static 	int 	_gTraceLvl_LoadCtrl = TRACELVL_WARNING;


/*	---------------------------------------------------------------------------
**	The Load Control's Module
*/
int 	SLoadCtrl_ModAdd(_t_loadctrl_agt  * pAgent, _t_loadctrl_reg  * pModReg)
{
	_t_loadctrl_mod * 	pModule;
	_t_blist 			* 	pQue;
	_t_bnode	    	* 	pNode;
	int 				i, hashKey, ret;

	ret = -1;
	if(pAgent && pModReg){
		/* Check AppAgt Init */
		if(pAgent->bInit == 0){
			SYS_TRACE(_gTraceLvl_LoadCtrl, TRACELVL_ERROR, "LoadCtrl Agent Not Init.\n");
			return -1;
		}	

		SYS_ENTER_CS(&(pAgent->lock));
		/* Hash ModId to Check if Already Reg */
		hashKey = pModReg->modId %_SLOADCTRL_HASH;
		pQue = &(pAgent->hashQue[hashKey]);

		ret = 0;
		for(i=0, pNode=pQue->next; 
			i<pQue->count; 
			i++, pNode=pNode->next){
			pModule = _O_PARENT(_t_loadctrl_mod, hashNode, pNode);
			if(pModule->modId == pModReg->modId){
				ret = -1;
				break;
			}
		}

		if(ret == 0){
			/* Alloc a Module */
			pQue = &(pAgent->modPool);
			pModule = (_t_loadctrl_mod *)BLST_GET(pQue);
			
			if(pModule){
				pModule->modId = pModReg->modId;
				SYS_MEMCPY(&(pModule->modReg), pModReg, sizeof(_t_loadctrl_reg));
				
				/* Add to Que & Hash */
				BLST_ADD2T(&(pAgent->modQue), &(pModule->modNode));
				pQue = &(pAgent->hashQue[hashKey]);
				BLST_ADD2T(pQue, &(pModule->hashNode));
			}
			else{
				SYS_TRACE(_gTraceLvl_LoadCtrl, TRACELVL_ERROR, "LoadCtrl Agent Module Alloc Fail.\n");
				ret = -1;
			}
		}
		else{
			SYS_TRACE(_gTraceLvl_LoadCtrl, TRACELVL_ERROR, "LoadCtrl Agent ModId Dup %d.\n", pModReg->modId);
			ret = -1;
		}
		
		SYS_LEAVE_CS(&(pAgent->lock));
	}

	return ret;
}


int 	SLoadCtrl_ModDel(_t_loadctrl_agt * pAgent, int modId)
{
	_t_loadctrl_mod * 	pModule;
	_t_blist 			* 	pQue;
	_t_bnode	    	* 	pNode;
	int 				i, hashKey, ret;
	
	/* Check AppAgt Init */
	if(!pAgent || (pAgent->bInit == 0)){
		SYS_TRACE(_gTraceLvl_LoadCtrl, TRACELVL_ERROR, "LoadCtrl Agent Not Init.\n");
		return -1;
	}	

	ret = -1;
	
	SYS_ENTER_CS(&(pAgent->lock));
	/* Hash ModId to Check if Already Reg */
	hashKey = modId%_SLOADCTRL_HASH;
	pQue = &(pAgent->hashQue[hashKey]);
	
	for(i=0, pNode=pQue->next; 
		i<pQue->count; 
		i++, pNode=pNode->next){
		pModule = _O_PARENT(_t_loadctrl_mod, hashNode, pNode);
		if(pModule->modId == modId){
			ret = 0;
			break;
		}
	}
	
	if((ret == 0) && pModule){
		BLST_DEL(pQue, &(pModule->hashNode));
		BLST_DEL(&(pAgent->modQue), &(pModule->modNode));
		/* Free 2 Pool */
		BLST_ADD2H(&(pAgent->modPool), &(pModule->modNode));
	}
	SYS_LEAVE_CS(&(pAgent->lock));

	return ret;
}


static  int 	_SLoadCtrl_ModExec(_t_loadctrl_mod * pModule, int index, _t_loadctrl_val * pMeter)
{
	_t_loadctrl_cb * pCb;
	if(pModule && pMeter){
		_LOADCTRL_VAL_INIT(pMeter, 0);

		/* Measure */
		pCb =  &(pModule->modReg.cbDefault);
		if(pCb->cbMeasure.cbProc){
			/* Record the Meter */
			pMeter->val = pCb->cbMeasure.cbProc(pCb->cbMeasure.pArg, pCb->cbMeasure.lArg, index);
		}
		return 0;
	}

	return -1;
}


static  int 	_SLoadCtrl_ModNotify(_t_loadctrl_mod * pModule, int index, unsigned int mVal, int type)
{
	_t_loadctrl_cb * pCb;
	if(pModule){
		/* Notify*/
		pCb =  &(pModule->modReg.cbDefault);
		if(pCb->cbNotify.cbProc){
			/* Notify the Meter */
			pCb->cbNotify.cbProc(pCb->cbMeasure.pArg, pCb->cbMeasure.lArg, index, type, mVal);
		}
		return 0;
	}

	return -1;
}


/*	---------------------------------------------------------------------------
**	The Load Control's Agent
*/
int 	SLoadCtrl_AgtInit(_t_loadctrl_agt * pAgent, int index)
{
	int 				  i;
	_t_loadctrl_mod * pModule;
	if(pAgent){
		/* Init the Lock*/
		SYS_SEM_B_INIT(&(pAgent->wake));
		SYS_INIT_CS(&(pAgent->lock));

		/* Init the Queue */
		for(i=0; i<_SLOADCTRL_HASH; i++){
			BLIST_INIT(&(pAgent->hashQue[i]));
		}
		BLIST_INIT(&(pAgent->modQue));
		BLIST_INIT(&(pAgent->modPool));
		
		for(i=0, pModule=pAgent->module; i<_SLOADCTRL_MOD_MAX; i++, pModule++){
			_BList_Add2T(&(pAgent->modPool), &(pModule->modNode));
		}
		pAgent->pMan 	= NULL;
		pAgent->index 	= index;
		pAgent->bInit 	= 1;
		pAgent->bOL 	= 0;
		return 0;
	}

	return -1;
}


int 	SLoadCtrl_AgtExec(_t_loadctrl_manager * pMan, _t_loadctrl_agt * pAgent, _t_loadctrl_val * pMeter)
{
	_t_loadctrl_mod * 	pModule;
	_t_blist 			* 	pQue;
	_t_bnode	    	* 	pNode;
	int 				i, 	ret;
	unsigned int 		weight, sw;
	_t_loadctrl_val 	meter;
	_t_loadctrl_val 	sum;

	ret = -1;
	if(pMan && (pMan->bInit == 1) && pAgent && (pAgent->bInit == 1)){
		ret = 0;
		sw = 0;
		_LOADCTRL_VAL_INIT(&sum, 0);
		_LOADCTRL_VAL_INIT(&meter, 0);	
		
		SYS_ENTER_CS(&(pAgent->lock));
		pQue = &(pAgent->modQue);
		for(i=0, pNode=pQue->next;  i<pQue->count;  i++, pNode=pNode->next){
			/* Measure the Module */
			pModule = _O_PARENT(_t_loadctrl_mod, modNode, pNode);
			_SLoadCtrl_ModExec(pModule, pAgent->index, &meter);
			
			/* Multi the Weight */
			weight 	=  pModule->modReg.weight;
			sw 		+= weight; 
			sum.val += (weight * meter.val);
		}
		if(sw != 0){
			sum.val /= sw;
		}
		
		_LOADCTRL_VAL_ASSIGN(pMeter, &sum);
		
		/* Now Check the Threshold */
		if(sum.val >= pMan->uThrs.val){
			pAgent->bOL = 1;
			for(i=0, pNode=pQue->next;	i<pQue->count;	i++, pNode=pNode->next){
				/* Measure the Module */
				pModule = _O_PARENT(_t_loadctrl_mod, modNode, pNode);
				_SLoadCtrl_ModNotify(pModule, pAgent->index, sum.val, _LC_TYPE_ABOVE);
			}
		}

		else if((sum.val < pMan->dThrs.val) && (pAgent->bOL == 1)){
			pAgent->bOL = 0;
			for(i=0, pNode=pQue->next;	i<pQue->count;	i++, pNode=pNode->next){
				/* Measure the Module */
				pModule = _O_PARENT(_t_loadctrl_mod, modNode, pNode);
				_SLoadCtrl_ModNotify(pModule, pAgent->index, sum.val, _LC_TYPE_BELOW);
			}
		}

		SYS_LEAVE_CS(&(pAgent->lock));
	}

	return ret;
}


int 	SLoadCtrl_AgtAdd(_t_loadctrl_manager * pMan, _t_loadctrl_agt * pAgent)
{
	if(pMan && (pMan->bInit == 1) && pAgent && (pAgent->bInit == 1)){
		SYS_ENTER_CS(&(pMan->lock));
		BLST_ADD2T(&(pMan->agtQue), &(pAgent->agtNode));
		pAgent->pMan = pMan;
		SYS_LEAVE_CS(&(pMan->lock));
	}

	return -1;
}


int 	SLoadCtrl_AgtDel(_t_loadctrl_manager * pMan, _t_loadctrl_agt * pAgent)
{
	if(pMan && (pMan->bInit == 1) && pAgent && (pAgent->bInit == 1)){
		SYS_ENTER_CS(&(pMan->lock));
		pAgent->pMan = NULL;
		BLST_DEL(&(pMan->agtQue), &(pAgent->agtNode));
		SYS_LEAVE_CS(&(pMan->lock));
	}

	return -1;
}

/*	---------------------------------------------------------------------------
**	The Load Control's Manager
*/
#define _LOADCTRL_VAL_RATIO 	2

int		SLoadCtrl_ManInit(_t_loadctrl_manager * pMan, _t_loadctrl_val * uLimit, _t_loadctrl_val * dLimit)
{
	if(pMan && uLimit && dLimit){
		/* Init the Lock*/
		SYS_SEM_B_INIT(&(pMan->wake));
		SYS_INIT_CS(&(pMan->lock));

		/* Init the Queue */
		BLIST_INIT(&(pMan->agtQue));

		/* Check & Init the Threshold */
		if(dLimit->val == 0){
			dLimit->val = 1;
		}
		if(uLimit->val <= (dLimit->val * _LOADCTRL_VAL_RATIO)){
			uLimit->val = (dLimit->val * _LOADCTRL_VAL_RATIO);
		}
		
		_LOADCTRL_VAL_ASSIGN(&(pMan->uThrs), uLimit);
		_LOADCTRL_VAL_ASSIGN(&(pMan->dThrs), dLimit);

		pMan->bInit = 1;
		return 0;
	}

	return -1;
}


int 	SLoadCtrl_ManThrsSet(_t_loadctrl_manager * pMan, _t_loadctrl_val * uLimit, _t_loadctrl_val * dLimit)
{
	if(pMan && (pMan->bInit == 1) && uLimit && dLimit){
		/* Check & Init the Threshold */
		if(dLimit->val == 0){
			dLimit->val = 1;
		}
		if(uLimit->val <= (dLimit->val * _LOADCTRL_VAL_RATIO)){
			uLimit->val = (dLimit->val * _LOADCTRL_VAL_RATIO);
		}

		_LOADCTRL_VAL_ASSIGN(&(pMan->uThrs), uLimit);
		_LOADCTRL_VAL_ASSIGN(&(pMan->dThrs), dLimit);

		return 0;
	}

	return -1;
}


int 	SLoadCtrl_ManThrsGet(_t_loadctrl_manager * pMan, _t_loadctrl_val * uLimit, _t_loadctrl_val * dLimit)
{
	if(pMan && (pMan->bInit == 1) && uLimit && dLimit){
		/* Init the Threshold */
		_LOADCTRL_VAL_ASSIGN(uLimit, &(pMan->uThrs));
		_LOADCTRL_VAL_ASSIGN(dLimit, &(pMan->dThrs));

		return 0;
	}

	return -1;
}

